import { Part, Stream } from "../_type";

interface SliderItemNode {
	display_url: string;
	is_video: boolean;
	video_url: string;
}

class SliderItemNode implements SliderItemNode {
	constructor(
		public display_url: string,
		public is_video: boolean,
		public video_url: string,
	) {}

	extractMediaURL(): string {
		return this.is_video ? this.video_url : this.display_url;
	}
}

interface InstagramPayload {
	shortcode_media: {
		id: string;
		edge_sidecar_to_children: {
			edges: Array<{ node: SliderItemNode }>;
		};
	};
}

class InstagramPayload implements InstagramPayload {
	constructor(public shortcode_media: InstagramPayload["shortcode_media"]) {}

	isEmpty(): boolean {
		return this.shortcode_media.id === "";
	}
}
async function getPostWithCode(code: string): Promise<string[]> {
	const URL = `https://www.instagram.com/p/${code}/embed/captioned/`;

	let embeddedMediaImage = "";
	let embedResponse = new InstagramPayload({
		id: "",
		edge_sidecar_to_children: { edges: [] },
	});

	try {
		const response = await fetch(URL, {
			headers: {
				"User-Agent":
					"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
			},
		}).then((res) => res.text());

		const imgMatch = response.match(/<img[^>]+class="EmbeddedMediaImage"[^>]+src="([^"]+)"/);
		if (imgMatch && imgMatch[1]) {
			embeddedMediaImage = imgMatch[1];
		}

		const scriptMatch = response.match(/"gql_data":([\s\S]*?)\}\"\}\]\]\,\[\"NavigationMetrics/);
		if (scriptMatch && scriptMatch[1]) {
			let s = scriptMatch[1]
				.replace(/\\"/g, '"')
				.replace(/\\\//g, "/")
				.replace(/\\\\/g, "\\");
			embedResponse = JSON.parse(s);
		}

		if (!embedResponse.isEmpty()) {
			return embedResponse.shortcode_media.edge_sidecar_to_children.edges.map(
				(edge) => edge.node.extractMediaURL(),
			);
		}

		if (embeddedMediaImage !== "") {
			return [embeddedMediaImage];
		}

		throw new Error(
			'Failed to fetch the post, the page might be "private", or the link is completely wrong',
		);
	} catch (error) {
		throw new Error(`Failed to send HTTP request to Instagram: ${error}`);
	}
}

function extractShortCodeFromLink(link: string): string {
	const match = link.match(/(p|tv|reel|reels\/videos)\/([A-Za-z0-9-_]+)/);
	if (!match || match.length !== 3) {
		throw new Error("Couldn't extract the media short code from the link");
	}
	return match[2];
}

const getFileSize = async (url: string): Promise<number> => {
	try {
		const response = await fetch(url, { method: "HEAD" });

		return parseInt(response.headers.get("content-length") || "0", 10);
	} catch (error) {
		console.error(`Failed to get file size for ${url}: ${error}`);
		return 0;
	}
};

export default async (url: string) => {
	try {
		const parsedUrl = new URL(url);
		const shortCode = extractShortCodeFromLink(parsedUrl.toString());
		const urls = await getPostWithCode(shortCode);

		let totalSize = 0;
		const parts: Part[] = [];

		for (const u of urls) {
			const ext = u.split(".").pop() || "";
			const fileSize = await getFileSize(u);

			const part: Part = {
				url: u,
				size: fileSize,
				ext: ext,
			};
			parts.push(part);
			totalSize += fileSize;
		}

		const streams: { [key: string]: Stream } = {
			default: {
				parts: parts,
				size: totalSize,
			},
		};

		return [
			{
				site: "Instagram instagram.com",
				title: `Instagram ${shortCode}`,
				type: "image",
				streams: streams,
				url: url,
			},
		];
	} catch (error) {
		throw new Error(`Extraction failed: ${error}`);
	}
};
